package com.lipaluma.util;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.LinkedBlockingQueue;

import com.lipaluma.exceptions.CsvParserException;

/**
 * Utility class for reflection
 * @author mario.soares
 */
public class ReflectionUtils {

	public static Collection<Object> instantiateCollection(Class<?> type) {
		if (Set.class.isAssignableFrom(type)) {
			if(type.isInterface())
				return new HashSet<Object>();
			else 
				return doInstanciation(type);
		}
		else if (Queue.class.isAssignableFrom(type)) {
			if(type.isInterface())
				return new LinkedBlockingQueue<Object>();
			else
				return doInstanciation(type);
		}
		else if (Collection.class.isAssignableFrom(type)) {
			if(type.isInterface())
				return new ArrayList<Object>();
			else
				return doInstanciation(type);
		}
		else
			throw new CsvParserException("The instanciation of "+type+" is not managed");
	}

	@SuppressWarnings("unchecked")
	private static Collection<Object> doInstanciation(Class<?> type) {
		try {
			return (Collection<Object>)type.newInstance();
		} catch (Exception e) {
			throw new CsvParserException(type + " can't be instanciated", e);
		}
	}
	
	public static Class<?> getParameterizedClass(Field field) {
		if(field.getType().isArray())
			return field.getType().getComponentType();
		else if (field.getGenericType() instanceof ParameterizedType) {
			java.lang.reflect.Type type = ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0];
			if (type instanceof Class)
				return (Class<?>) type;
			else if (type instanceof ParameterizedType) {
				return (Class<?>) ((ParameterizedType) type).getRawType();
			} else
				throw new CsvParserException("Error on field "+field.getName()+" : The parameterized Type " + type.toString() + " is not managed.");
		} else
			return field.getType();
	}
	
	public static void makeAccessible(Field field) {
		org.springframework.util.ReflectionUtils.makeAccessible(field);
	}

	public static void makeAccessible(Method method) {
		org.springframework.util.ReflectionUtils.makeAccessible(method);
	}
	
	public static Object getField(Field field, Object target) {
		return org.springframework.util.ReflectionUtils.getField(field, target);
	}
	
	public static void setField(Field field, Object target, Object value) {
		org.springframework.util.ReflectionUtils.setField(field, target, value);
	}

	public static Object invokeMethod(Method method, Object target) {
		return org.springframework.util.ReflectionUtils.invokeMethod(method, target);
	}

	public static Object invokeMethod(Method method, Object target, Object... args) {
		return org.springframework.util.ReflectionUtils.invokeMethod(method, target, args);
	}

	public static Method findMethod(Class<? extends Object> clazz, String name) {
		return org.springframework.util.ReflectionUtils.findMethod(clazz, name);
	}

	public static Field findField(Class<? extends Object> clazz, String name) {
		return org.springframework.util.ReflectionUtils.findField(clazz, name);
	}
}
